﻿using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;


namespace KunURP
{
    public class DepthNormalFeature : ScriptableRendererFeature
    {
        class CustomRenderPass : ScriptableRenderPass
        {
            private RenderTargetHandle m_Destination;
            private Material m_DepthNormalMaterial = null;

            private FilteringSettings m_FilteringSettings;
            private ShaderTagId m_ShaderTagID = new ShaderTagId("DepthOnly");

            public CustomRenderPass(RenderQueueRange renderQueueRange, LayerMask layerMask, Material material)
            {
                this.m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);
                this.m_DepthNormalMaterial = material;
            }

            public void Setup(RenderTargetHandle destination)
            {
                this.m_Destination = destination;
            }

            // This method is called before executing the render pass.
            // It can be used to configure render targets and their clear state. Also to create temporary render target textures.
            // When empty this render pass will render to the active camera render target.
            // You should never call CommandBuffer.SetRenderTarget. Instead call <c>ConfigureTarget</c> and <c>ConfigureClear</c>.
            // The render pipeline will ensure target setup and clearing happens in an performance manner.
            public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
            {
                cameraTextureDescriptor.depthBufferBits = 32;
                cameraTextureDescriptor.colorFormat = RenderTextureFormat.ARGB32;
                cmd.GetTemporaryRT(this.m_Destination.id, cameraTextureDescriptor, FilterMode.Point);
                ConfigureTarget(this.m_Destination.Identifier());
                ConfigureClear(ClearFlag.All, Color.black);
            }

            // Here you can implement the rendering logic.
            // Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
            // https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
            // You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
            public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
            {
                CommandBuffer cmd = CommandBufferPool.Get("深度法线获取Pass");
                using (new ProfilingScope(cmd, new ProfilingSampler("DepthNormals Prepass")))
                {
                    context.ExecuteCommandBuffer(cmd);
                    cmd.Clear();

                    var sortFlags = renderingData.cameraData.defaultOpaqueSortFlags;
                    var drawingSettings = CreateDrawingSettings(this.m_ShaderTagID, ref renderingData, sortFlags);
                    drawingSettings.perObjectData = PerObjectData.None;
                    ref CameraData cameraData = ref renderingData.cameraData;
                    Camera camera = cameraData.camera;
                    if (cameraData.isStereoEnabled)
                        context.StartMultiEye(camera);
                    drawingSettings.overrideMaterial = this.m_DepthNormalMaterial;
                    context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref this.m_FilteringSettings);
                    cmd.SetGlobalTexture("_CameraDepthNormalsTexture", this.m_Destination.id);
                }
                context.ExecuteCommandBuffer(cmd);
                CommandBufferPool.Release(cmd);
            }

            /// Cleanup any allocated resources that were created during the execution of this render pass.
            public override void FrameCleanup(CommandBuffer cmd)
            {
                if (this.m_Destination != RenderTargetHandle.CameraTarget)
                {
                    cmd.ReleaseTemporaryRT(this.m_Destination.id);
                    this.m_Destination = RenderTargetHandle.CameraTarget;
                }
            }
        }

        CustomRenderPass m_ScriptablePass;
        RenderTargetHandle m_DepthNormalTexture;
        Material m_Material;

        public override void Create()
        {
            this.m_Material = CoreUtils.CreateEngineMaterial("Hidden/Internal-DepthNormalsTexture");
            this.m_ScriptablePass = new CustomRenderPass(RenderQueueRange.opaque, -1, this.m_Material);
            this.m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingPrePasses;
            this.m_DepthNormalTexture.Init("_CameraDepthNormalsTexture");
        }

        // Here you can inject one or multiple render passes in the renderer.
        // This method is called when setting up the renderer once per-camera.
        public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
        {
            this.m_ScriptablePass.Setup(this.m_DepthNormalTexture);
            renderer.EnqueuePass(this.m_ScriptablePass);
        }
    }
}

